package com.ccteam.admin.utils

import android.app.Application
import android.content.Context
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import cn.hutool.core.util.IdUtil
import com.orhanobut.logger.Logger
import com.qiniu.android.storage.UpCancellationSignal
import com.qiniu.android.storage.UpCompletionHandler
import com.qiniu.android.storage.UpProgressHandler
import com.qiniu.android.storage.UploadManager
import com.qiniu.android.storage.UploadOptions
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import javax.inject.Inject
import kotlin.random.Random

/**
 * @author Xiaoc
 * @since 2021/3/19
 *
 * 上传文件到七牛云的工具类，它简化了上传文件需要写的代码，规范了上传流程的处理
 */
class UploadFileUtils @Inject constructor(
    private val uploadManager: UploadManager
) {

    companion object{
        private const val TAG = "UploadFileUtils"
    }

    @Volatile
    private var shouldCancel: Boolean = false

    /**
     * 上传
     * 需要传入上传需要的必要元素 token，上传文件Url，以及各种回调
     * @param progressUpdate 进度更新回调
     * @param uploadFail 上传失败回调
     * @param uploadSuccess 上传成功回调
     */
    suspend fun upload(
        application: Application,token: String?, uploadUrl: String?,
                   progressUpdate: (progress: Int) -> Unit,
                   uploadSuccess: (fileUrl: String) -> Unit,
                   uploadFail: (errorMessage: String) -> Unit){
        withContext(Dispatchers.IO){
            try {
                uploadFile(application,token,uploadUrl,progressUpdate,
                    uploadSuccess, uploadFail)
            } catch (e: Exception){
                uploadFail("上传出错：${e.message}")
            }
        }
    }

    private fun uploadFile(
        application: Application,
        token: String?, uploadUrl: String?,
        progressUpdate: (progress: Int) -> Unit,
        uploadSuccess: (fileUrl: String) -> Unit,
        uploadFail: (errorMessage: String) -> Unit
    ){
        if(token.isNullOrEmpty() || uploadUrl.isNullOrEmpty()){
            Logger.d("${TAG}：上传文件地址或Token为空")
            uploadFail("上传文件地址或Token为空")
            return
        }
        val uploadUri = Uri.parse(uploadUrl)
        val file = DocumentFile.fromSingleUri(application.applicationContext,
            uploadUri)
        if(file == null){
            Logger.d("${TAG}：文件内容为空")
            uploadFail("文件内容为空")
            return
        }

        val fileByte = application.contentResolver.openInputStream(uploadUri)?.readBytes()
        if(fileByte == null || fileByte.isEmpty()){
            Logger.d("${TAG}：文件流获取为空")
            uploadFail("文件流获取为空")
            return
        }

        val mimeType = file.type
        val fileName = IdUtil.simpleUUID() + "." + file.parseMimeType()

        try {
            uploadManager.put(fileByte,fileName,token,
                UpCompletionHandler { _, info, response ->
                    if(info.isOK){
                        uploadSuccess(response.getString("data"))
                    } else {
                        Logger.d("${TAG}：上传失败：$info.error")
                        uploadFail(info.error)
                    }
                }, UploadOptions(null,mimeType,false,
                    UpProgressHandler { _ , percent ->
                        progressUpdate((percent * 100).toInt())
                    }, UpCancellationSignal {
                        return@UpCancellationSignal shouldCancel
                    })
            )
        } catch (e: Exception){
            Logger.d("${TAG}：上传失败：${e.message}")
            uploadFail(e.message ?: "未知错误")
        }

    }

    /**
     * 上传
     * 需要传入上传需要的必要元素 token，上传文件Url，以及各种回调
     * 此方法还传入了一个index，用于让其知道当前上传的索引顺序，会跟着上传成功回调传出
     *
     * 此方法用于循环上传内容，index可能会为其提供帮助
     *
     * @param index 当前上传索引数
     * @param progressUpdate 进度更新回调
     * @param uploadFail 上传失败回调
     * @param uploadSuccess 上传成功回调
     */
    suspend fun upload(
        application: Application,token: String?,
        index: Int,
        uploadUrl: String?,
        progressUpdate: (progress: Int) -> Unit,
        uploadSuccess: (index: Int,fileUrl: String) -> Unit,
        uploadFail: (errorMessage: String) -> Unit){

        withContext(Dispatchers.IO){
            uploadFile(application,index,token,uploadUrl,progressUpdate,
                uploadSuccess, uploadFail)
        }

    }

    private fun uploadFile(
        application: Application,
        index: Int,
        token: String?, uploadUrl: String?,
        progressUpdate: (progress: Int) -> Unit,
        uploadSuccess: (index: Int,fileUrl: String) -> Unit,
        uploadFail: (errorMessage: String) -> Unit
    ){
        if(token.isNullOrEmpty() || uploadUrl.isNullOrEmpty()){
            Logger.d("${TAG}：上传文件地址或Token为空")
            uploadFail("上传文件地址或Token为空")
            return
        }
        val uploadUri = Uri.parse(uploadUrl)
        val file = DocumentFile.fromSingleUri(application.applicationContext,
            uploadUri)
        if(file == null){
            Logger.d("${TAG}：文件内容为空")
            uploadFail("文件内容为空")
            return
        }

        val fileByte = application.contentResolver.openInputStream(uploadUri)?.readBytes()
        if(fileByte == null || fileByte.isEmpty()){
            Logger.d("${TAG}：文件流获取为空")
            uploadFail("文件流获取为空")
            return
        }

        val mimeType = file.type
        val fileName = IdUtil.simpleUUID() + "." + file.parseMimeType()

        try {
            uploadManager.put(fileByte,fileName,token,
                UpCompletionHandler { _, info, response ->
                    if(info.isOK){
                        uploadSuccess(index,response.getString("data"))
                    } else {
                        Logger.d("${TAG}：上传失败：$info.error")
                        uploadFail(info.error)
                    }
                }, UploadOptions(null,mimeType,false,
                    UpProgressHandler { _ , percent ->
                        progressUpdate((percent * 100).toInt())
                    }, UpCancellationSignal {
                        return@UpCancellationSignal shouldCancel
                    })
            )
        } catch (e: Exception){
            Logger.d("${TAG}：上传失败：${e.message}")
            uploadFail(e.message ?: "未知错误")
        }

    }

    fun cancelUpload(){
        shouldCancel = true
    }
}